package com.example.demo.util;

import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import com.google.common.primitives.Longs;
import java.nio.charset.Charset;

public class RedisBloomFilter {


    private int numApproxElements;  //预计容量
    private double fpp;             //误判率
    private int numHashFunctions;   //计算几次hash
    private int bitmapLength;       //布隆过滤器的容量

    /**
     * 构造布隆过滤器。注意：在同一业务场景下，三个参数务必相同
     *
     * @param numApproxElements 预估元素数量
     * @param fpp 可接受的最大误差（假阳性率）
     */
    public RedisBloomFilter(int numApproxElements, double fpp) {
        this.numApproxElements = numApproxElements;
        this.fpp = fpp;

        bitmapLength = (int) (-numApproxElements * Math.log(fpp) / (Math.log(2) * Math.log(2)));
        numHashFunctions = Math.max(1, (int) Math.round((double) bitmapLength / numApproxElements * Math.log(2)));
    }

    /**
     * 取得自动计算的最优哈希函数个数
     */
    public int getNumHashFunctions() {
        return numHashFunctions;
    }

    /**
     * 取得自动计算的最优Bitmap长度
     */
    public int getBitmapLength() {
        return bitmapLength;
    }

    /**
     * 计算一个元素值哈希后映射到Bitmap的哪些bit上
     *
     * @param element 元素值
     * @return bit下标的数组
     */
    public Long[] getBitIndices(String element) {
        Long[] indices = new Long[numHashFunctions];

        byte[] bytes = Hashing.murmur3_128()
            .hashObject(element, Funnels.stringFunnel(Charset.forName("UTF-8")))
            .asBytes();

        long hash1 = Longs.fromBytes(
            bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]
        );
        long hash2 = Longs.fromBytes(
            bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]
        );

        long combinedHash = hash1;
        for (int i = 0; i < numHashFunctions; i++) {
            indices[i] = (combinedHash & Long.MAX_VALUE) % bitmapLength;
            combinedHash += hash2;
        }

        return indices;
    }


}